#!/usr/bin/python2
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011 5sing-crawler contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


__author__ = "antiAgainst@gmail.com"

import re
import os.path

#import urllib
import cookielib
import mechanize

import lxml.html.soupparser as sp

control_chars = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
                0x07, 0x08, 0x0E, 0x0F, 0x10, 0x11, 0x12,
                0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
                0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F]
chars = [chr(c) for c in control_chars]
ccre = re.compile('[%s]' % ''.join(chars))

class FiveSingClient(object):
    """ 
    Client for crawling music from 5sing.com
    
    to use, firstly
        client = FiveSingClient()
    and
        client.login(some_id, some_password)
    then
        client.crawl_user()
    """
    def __init__(self):
        self.br = mechanize.Browser()

        # different parameters of the browser
        self.br.set_handle_equiv(True)
        self.br.set_handle_gzip(False)
        self.br.set_handle_redirect(True)
        self.br.set_handle_referer(True)
        self.br.set_handle_robots(False)

        # print info about http header/response?
        self.br.set_debug_http(False)
        # print info about redirect?
        self.br.set_debug_redirects(False)
        # print debug response?
        self.br.set_debug_responses(False)

        # add firfox-like headers
        self.br.addheaders = [
            ("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0"), 
            ("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"),
            ("Accept-Language", "en-us,en;q=0.5"), 
            ("Accept-Charset", "gb18030,utf-8;q=0.7,*;q=0.7")]

    def login(self, id, passwd):
        # create cookie for the current user
        cj = cookielib.CookieJar()
        self.br.set_cookiejar(cj)

        # open the login web page
        self.br.open("http://www.5sing.com/login.aspx")
        self.br.select_form(name='ctl00')

        # fill in the form
        self.br["txtUserName"] = id
        self.br["txtPassword"] = passwd

        # submit the form
        self.br.submit()

    def get_music_url(self, start_url):
        """ get a singer's music's name(s) and id(s) """
        self.br.open(start_url)
        body = self.br.response().read()
        #dom = sp.fromstring(body)
        dom = sp.fromstring(ccre.sub('', body))

        # N piece of music
        tot = int(dom.xpath('//div[@class="page_list"]/span[1]/text()')[0])
        print "\n" + "-" * 15 + "%d piece(s) of music" % tot + "-" * 15
        current = 0 # how many crawled
        page = 1
        music_dict = {} # music-id <--> music-name dict

        while current < tot:
            print "\n---[request] %s---\n" % (start_url + '?p=%d' % page)
            self.br.open(start_url + '?p=%d' % page)
            body = self.br.response().read()
            #dom = sp.fromstring(body)
            dom = sp.fromstring(ccre.sub('', body))
            dom = dom.xpath('//table[@class="song"]/descendant::tr/td[2]/a')
            for music in dom:
                name = music.get('title')
                url = music.get('href')
                id = url.split('/')[-1].split('.')[0]
                print "[id] %s [name] %s" % (id, name)
                music_dict[id] = name
            current += len(dom)
            page += 1
        return music_dict

    def download_music(self, type, music, prefix):
        for id in music:
            print "---[request] %s---" % (id)
            self.br.open("http://%s.5sing.com/Down.aspx?sid=%s" % (type, id))
            body = self.br.response().read()
            #dom = sp.fromstring(body)
            dom = sp.fromstring(ccre.sub('', body))

            name = dom.xpath('//div[@class="main ft"]/h1/a')[0].text
            p = dom.xpath('//div[@class="main ft"]/p')
            dtype = p[0].xpath('em')
            if len(dtype) == 2 and dtype[1].text == u'免费下载':
                singer = p[0].text.split(u'：')[1]
                ftype = p[0].xpath('text()')[1].split(u'：')[1]
                url = dom.xpath('//a[@class="d_sing"]/@href')[0]
                print "[id]%s [mime]%s [singer]%s" % (id, ftype, singer)
                save_name = '%s-%s.%s' % (name, singer, ftype)
                #save_name = urllib.quote(save_name, safe='=+,;')
                save_name = save_name.replace('/', '-')
                save_name = "%s/%s" % (prefix, save_name)
                if not os.path.exists(save_name):
                    print '[download]%s' % save_name
                    self.br.retrieve(url, save_name)
                else:
                    print "[info]exists, skip"

    def crawl_user(self, id, prefix):
        print "\n" + "-" * 15 + "http://%s.5sing.com/ycsong.aspx" % id + "-" * 15
        md = self.get_music_url('http://%s.5sing.com/YcSong.aspx' % id)
        print
        self.download_music('yc', md, prefix)

        print "\n" + "-" * 15 + "http://%s.5sing.com/fcsong.aspx" % id + "-" * 15
        md = self.get_music_url('http://%s.5sing.com/fcsong.aspx' % id)
        print
        self.download_music('fc', md, prefix)

def demo():
    """ demo: get 清漪's music """
    import getpass

    print "[info]Your 5sing.com account is required to download music"
    id = raw_input("[userID]:")
    passwd = getpass.getpass("[password]:")

    client = FiveSingClient()
    client.login(id, passwd)

    print "[info]fetching 清漪(3142067)'s music"
    client.crawl_user('3142067')

def fetch_multi():
    """ fetch many singers' songs """
    import getpass
    import os

    print "[info]Your 5sing.com account is required to download music"
    id = raw_input("[userID]:")
    passwd = getpass.getpass("[password]:")

    client = FiveSingClient()
    client.login(id, passwd)

    singerid = raw_input("[singerID]:")
    while singerid:
        if not os.path.isdir(singerid):
            os.mkdir(singerid)
        client.crawl_user(singerid, singerid)
        singerid = raw_input("[singerID]:")


if __name__ == "__main__":
    fetch_multi()


__all__ = ['FiveSingClient']
